package com.reider.log4AS3.deciders
{
	import com.reider.log4AS3.dataholder.Level;
	import com.reider.log4AS3.dataholder.LevelHolder;
	import com.reider.log4AS3.dataholder.LoggerScope;
	import com.reider.log4AS3.interfaces.ILoggerScopeDecider;
	
	import flash.utils.Dictionary;

	public class CachedTreeDecider implements ILoggerScopeDecider
	{
		protected var scopeCache:Dictionary
		protected var cacheClearAllowed:Boolean = true
		
		private var _defaultLevel:Level
		private var _rootNode:LoggerScopeNode
		
		public function get defaultLevel():Level
		{
			return _defaultLevel || ( _defaultLevel = LevelHolder.ALL)
		}
		
		protected function get rootNode():LoggerScopeNode
		{
			if(!_rootNode){
				_rootNode = new LoggerScopeNode(
					null, new LoggerScope(true, null, defaultLevel)
				)
			}
			
			return _rootNode
		}
		
		//--------------------------------------------------------------------------
		//
		//  Constructor
		//
		//--------------------------------------------------------------------------
		
		public function CachedTreeDecider()
		{
			scopeCache = new Dictionary()
		}
		
		//--------------------------------------------------------------------------
		//
		//  Public Methods
		//
		//--------------------------------------------------------------------------
		
		public function registerScopes(scopes:Array):void
		{
			cacheClearAllowed = false
				
			for each(var scope:LoggerScope in scopes){
				registerScope( scope )
			}
			
			cacheClearAllowed = true
			requestCacheClear()
		}
		
		public function registerScope(scope:LoggerScope):void
		{
			if(scope.isRoot){
				rootNode.scope = scope
				return
			}
			
			var parts:Array = getNameParts( scope.name )
			addScope( rootNode, parts, scope )
			
			requestCacheClear()
		}
		
		public function isNameInScope( name:String, requestedLevel:Level ):Boolean
		{
			var scope:LoggerScope = getTopScopeForName( name )
			if(scope){
				return scope.level.check( "<=" , requestedLevel )
			}
			
			return false
		}
		
		public function getTopScopeForName(name:String):LoggerScope
		{
			var parts:Array = getNameParts( name )
			var scope:LoggerScope = getTopScope( rootNode, parts )
				
			return scope
		}
		
		public function getValidScopes(name:String, level:Level):Array
		{
			var scopes:Array = new Array()
			var parts:Array = getNameParts( name )
			getValidScopesForLevel(rootNode, parts, level, scopes)
			
			return scopes
		}
		
		public function requestCacheClear():void
		{
			if(!cacheClearAllowed) return; 
			
			scopeCache = new Dictionary()
		}
		
		//--------------------------------------------------------------------------
		//
		//  Private Methods
		//
		//--------------------------------------------------------------------------
		
		private function getNameParts(name:String):Array
		{
			name = name.toLowerCase()
			var parts:Array = name.split(".")
			
			return parts
		}
		
		private function addScope(node:LoggerScopeNode, parts:Array, scope:LoggerScope):void
		{	
			if(parts.length == 0){
				node.scope = scope
				return
			}
			
			var curName:String = parts.shift()
			var scopeNode:LoggerScopeNode = node.children[ curName ]
			if(!scopeNode){
				scopeNode = new LoggerScopeNode(curName, null)
				node.addChild(scopeNode)
			}
			
			addScope(scopeNode, parts, scope)
		}
		
		private function getTopScope(node:LoggerScopeNode, parts:Array):LoggerScope
		{
			var curScope:LoggerScope = node.scope
			var foundScope:LoggerScope
			
			if(parts.length == 0){
				return curScope
			}
			
			var curName:String = parts.shift()
			var scopeNode:LoggerScopeNode = node.children[ curName ]
			if(scopeNode){
				foundScope = getTopScope(scopeNode, parts)
				if(foundScope){
					curScope = foundScope
				}
			}
			
			return curScope
		}
		
		private function getValidScopesForLevel(node:LoggerScopeNode, parts:Array, requestedLevel:Level, foundScopes:Array):void
		{
			if(node.scope && node.scope.level.check("<=" , requestedLevel )){
				foundScopes.push( node.scope )
			}
			
			if(parts.length == 0){
				return 
			}
			
			var curName:String = parts.shift()
			var scopeNode:LoggerScopeNode = node.children[ curName ]
			if(scopeNode){
				getValidScopesForLevel( scopeNode, parts, requestedLevel, foundScopes)
			}
		}
	}
}